/*
 * Copyright 2020 Mike Penz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mikepenz.iconics.animation;

import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.global.resource.solidxml.Theme;

import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.iconics.typeface.ITypeface;
import com.mikepenz.iconics.utils.KtExtensions;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author pa.gulko zTrap (28.11.2018)
 */
public class IconicsAnimatedDrawable extends IconicsDrawable {
    private final List<IconicsAnimationProcessor> processors = new ArrayList<>();

    /**
     * 构造方法
     *
     * @param component 组件
     */
    public IconicsAnimatedDrawable(Component component) {
        super(component);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param theme 主题
     */
    public IconicsAnimatedDrawable(Component component, Theme theme) {
        super(component, theme);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param context 主题
     */
    public IconicsAnimatedDrawable(Component component, Context context) {
        super(component, context);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param context 上下文
     * @param icon 字体
     */
    public IconicsAnimatedDrawable(Component component, Context context, Character icon) {
        super(component, context, icon);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param context 上下文
     * @param icon 字体
     */
    public IconicsAnimatedDrawable(Component component, Context context, String icon) {
        super(component, context, icon);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param context 上下文
     * @param icon 字体
     */
    public IconicsAnimatedDrawable(Component component, Context context, IIcon icon) {
        super(component, context, icon);
    }

    /**
     * 构造方法
     *
     * @param component 组件
     * @param context 上下文
     * @param typeface 字体文件
     * @param icon 字体
     */
    public IconicsAnimatedDrawable(Component component, Context context, ITypeface typeface, IIcon icon) {
        super(component, context, typeface, icon);
    }

    @Override
    protected void draw(Canvas canvas) {
        for (IconicsAnimationProcessor processor : processors) {
            processor.processorPreDraw(canvas, getIconBrush(), getContourBrush(),
                getBackgroundBrush(), getBackgroundContourBrush());
        }
        super.draw(canvas);
        Collections.reverse(processors);
        for (IconicsAnimationProcessor processor : processors) {
            processor.processPostDraw(canvas);
        }
    }

    /**
     * Attach an [processor][IconicsAnimationProcessor] to this drawable
     *
     * @param processor 动画
     * @return Drawable
     */
    public IconicsAnimatedDrawable processor(IconicsAnimationProcessor processor) {
        processor.setDrawable(this);
        processors.add(processor);
        return this;
    }

    /**
     * Attach an [processors][IconicsAnimationProcessor] to this drawable
     *
     * @param processors 动画
     * @return Drawable
     */
    public IconicsAnimatedDrawable processors(IconicsAnimationProcessor... processors) {
        if (processors == null || processors.length == 0) {
            return this;
        }
        for (IconicsAnimationProcessor it : processors) {
            processor(it);
        }
        return this;
    }

    @Override
    public void setIcon(IIcon icon) {
        super.setIcon(icon);
        if (getComponent() != null) {
            animateIn(getComponent());
        }
    }

    /**
     * 开始动画
     *
     * @param component 执行动画的组件
     * @return The runner which used for animations. Animations can be easily removed by calling [Runner.unset]
     */
    public Runner animateIn(Component component) {
        return KtExtensions.also(new Runner(), new KtExtensions.UnitBlock<Runner>() {
            @Override
            public void invoke(Runner it) {
                it.setFor(component, IconicsAnimatedDrawable.this);
            }
        });
    }

    /**
     * 动画执行器
     *
     * @since 2021-06-04
     */
    private static class Runner {
        private static final int SYNC_RATE = 16;
        private boolean isAttached = false;
        private WeakReference<Component> component = null;

        private final EventHandler animHandler = new EventHandler(EventRunner.getMainEventRunner()) {
            @Override
            protected void processEvent(InnerEvent event) {
                Component cp = (Component) event.object;
                cp.invalidate();
                if (isAttached && component.get() != null) {
                    InnerEvent event1 = InnerEvent.get();
                    event1.object = cp;
                    animHandler.sendEvent(event1, SYNC_RATE);
                }
            }
        };

        private final Component.BindStateChangedListener listener = new Component.BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component cp) {
                isAttached = true;
                if (isAttached && component.get() != null) {
                    InnerEvent event = InnerEvent.get();
                    event.object = cp;
                    animHandler.sendEvent(event);
                }
            }

            @Override
            public void onComponentUnboundFromWindow(Component cp) {
                isAttached = false;
                animHandler.removeAllEvent();
            }
        };

        /**
         * Setup all animations to provided drawable and view
         *
         * @param cp 组件
         * @param drawable 图片
         */
        public void setFor(Component cp, IconicsAnimatedDrawable drawable) {
            unset();
            this.component = new WeakReference<>(cp);
            if (cp.isBoundToWindow()) {
                listener.onComponentBoundToWindow(cp);
            }
            cp.setBindStateChangedListener(listener);
        }

        /**
         * Clear all animations from previously provided drawable and view
         */
        public void unset() {
            KtExtensions.let(component, new KtExtensions.ResultBlockInvoker<WeakReference<Component>, Void>() {
                @Override
                public Void invoke(WeakReference<Component> it) {
                    if (it == null || it.get() == null) {
                        return null;
                    }
                    it.get().removeBindStateChangedListener(listener);
                    it.clear();
                    component = null;
                    isAttached = false;
                    return null;
                }
            });
        }
    }
}
